const fs = require('fs');
const pathUtil = require('path');
const _ = require('lodash');
const {
	readJsonSync,
	filterExts,
	replaceDS,
	removeExt,
	filterExcludeItems,
} = require('./utils');

class MiniProgramResources {

	constructor(plugin, resources) {
		this.plugin = plugin;

		this.entryPoints = {};
		this.scripts = {};
		this.assets = {};
		this.jsons = {};

		this.pages = {};
		this.components = {};

		this.prevResources = resources || undefined;
		this.seekDate = null;
		this.diffPrevTime = 0;

		this.__difference__ = false;
	}

	statOf(path) {
		try {
			return fs.statSync(path);
		} catch (e) {
			return false;
		}
	}

	isScript(path) {
		return this.plugin.isScript(path);
	}

	isJson(path) {
		return this.plugin.isJson(path);
	}

	selectPool(path) {
		if (this.isScript(path)) return this.scripts;
		if (this.isJson(path)) return this.jsons;
		return this.assets;
	}


	addResources(...resources) {
		// if (!Array.isArray(resources))
		// 	resources = [resources];
		// _.uniq(_.flattenDeep(resources)).forEach(res => {
		// 	const pool = this.selectPool(res);
		// 	if (pool.indexOf(res) < 0) {
		// 		pool.push(res);
		// 	}
		// });
		// return this;
	}


	makeAssets(type, key, ignores) {
		const assets = {};
		const {plugin} = this;
		const {options} = plugin;
		if (!Array.isArray(ignores)) {
			if (_.isString(ignores))
				ignores = [ignores];
			else
				ignores = false
		}
		if (ignores.length < 0)
			ignores = false;

		plugin.getExts(type, key).forEach(ext => {
			const file = `${key}.${ext}`;
			if (ignores && ignores.indexOf(file) > -1)
				return;
			const path = this.plugin.srcOf(file);
			let add = true;
			if (options.checkStatExts[ext]) {
				const stat = this.statOf(path);
				add = stat && stat.isFile();
			}
			if (add)
				assets[plugin.makeKey(path, true)] = path;
		});
		plugin.getAdditionFiles(type, key).forEach(file => {
			if (ignores && ignores.indexOf(file) > -1)
				return;
			const path = this.plugin.srcOf(`${file}`);
			assets[plugin.makeKey(path, true)] = path;
		});
		return assets;
	}

	getAppJson() {
		if (this.plugin.options.mockMain) {
			return this.plugin.srcOf(this.plugin.options.mockMain);
		} else {
			return this.plugin.srcOf(this.plugin.options.main);
		}
	}

	seekApp() {
		this.seekDate = new Date();
		if (this.prevResources) {
			this.diffPrevTime = this.prevResources.seekDate.valueOf() - this.seekDate.valueOf();
		}
		console.time('seekApp');
		const type = 'app';
		const json = this.getAppJson();
		const js = this.plugin.srcOf(removeExt(this.plugin.options.main) + '.js');
		const key = this.plugin.makeKey(js, true);
		const jsonKey = this.plugin.makeKey(json, true);
		const stat = this.statOf(json);
		const assets = this.makeAssets(type, removeExt(key), 'app.json');

		assets[jsonKey] = json;

		if (stat && stat.isFile()) {
			this.entryPoints[key] = {key, jsonKey, type, js, json, assets};
			this.scripts[key] = js;
			this.assets = _.merge(this.assets, assets);

			const {pages, tabBar, subPackages, sitemapLocation} = readJsonSync(json);
			if (sitemapLocation)
				this.handleJson(sitemapLocation);
			if (pages && _.isArray(pages) && pages.length > 0)
				pages.forEach(page => this.seekPage(page));
			if (tabBar && tabBar.list && _.isArray(tabBar.list) && tabBar.list.length > 0)
				tabBar.list.forEach(item => this.seekTabBar(item));
			if (subPackages && _.isArray(subPackages) && subPackages.length > 0)
				subPackages.forEach(subPkg => this.seekSubPackage(subPkg));
		} else {
			throw new Error(`app json file ${path} not exists!`);
		}

		console.timeEnd('seekApp');
		return this;
	}


	seekTabBar(tabBar) {
		const {text, pagePath, iconPath, selectedIconPath} = tabBar;
		if (!this.pages[pagePath])
			console.error(`'${pagePath}' 未定义在 pages 中！`);

		this.handleImage(iconPath);
		this.handleImage(selectedIconPath);

		return this;
	}

	handleJson(path) {
		const json = this.plugin.srcOf(path);
		const key = this.plugin.makeKey(json, true);
		const stat = this.statOf(json);

		if (stat && stat.isFile()) {
			if (!this.assets[key])
				this.assets[key] = json;
		} else {
			console.error(`JSON文件 '${path}' 不存在！`);
		}
		return this;
	}

	handleImage(path) {
		const img = this.plugin.srcOf(path);
		const key = this.plugin.makeKey(img, true);
		const stat = this.statOf(img);

		if (stat && stat.isFile()) {
			if (!this.assets[key])
				this.assets[key] = img;
		} else {
			console.error(`图片 '${path}' 不存在！`);
		}
		return this;
	}

	seekPage(page) {
		const type = 'page';
		const json = this.plugin.srcOf(page + '.json');
		const js = this.plugin.srcOf(removeExt(page) + '.js');
		const key = this.plugin.makeKey(js, true);
		const jsonKey = this.plugin.makeKey(json, true);
		const stat = this.statOf(json);
		const assets = this.makeAssets(type, removeExt(key), js);

		if (stat && stat.isFile()) {
			this.entryPoints[key] = {key, jsonKey, type, js, json, assets};
			this.scripts[key] = js;
			this.assets = _.merge(this.assets, assets);
			this.pages[page] = key;

			const {usingComponents} = readJsonSync(json);
			_.keys((usingComponents || {})).forEach(name => {
				this.seekComponent(this.entryPoints[key], name, usingComponents[name]);
			});
		} else {
			console.warn(`page json file ${json} not exists!`);
		}
		return this;
	}

	seekComponent(entryData, componentName, component) {
		const type = 'component';
		const entryPath = entryData.js;
		const entryDir = pathUtil.dirname(entryPath);

		let componentPath = component;
		if (pathUtil.isAbsolute(componentPath)) {
			// key => '/components/any/any'
			componentPath = _.trim(componentPath, '\\/');
			componentPath = pathUtil.resolve(this.plugin.srcPath, componentPath);
		} else {
			// key => 'any'
			// key => '../../components/any/any'
			componentPath = pathUtil.resolve(entryDir, componentPath);
		}

		const json = this.plugin.srcOf(componentPath + '.json');
		const js = this.plugin.srcOf(removeExt(componentPath) + '.js');
		const key = this.plugin.makeKey(js, true);
		const jsonKey = this.plugin.makeKey(json, true);

		const stat = this.statOf(json);
		const assets = this.makeAssets(type, removeExt(key), js);

		if (this.entryPoints[key]) {
			return this;
		}
		if (stat && stat.isFile()) {
			this.entryPoints[key] = {key, jsonKey, type, js, json, assets};
			this.scripts[key] = js;
			this.assets = _.merge(this.assets, assets);

			const {usingComponents} = readJsonSync(json);
			_.keys((usingComponents || {})).forEach(name => {
				this.seekComponent(this.entryPoints[key], name, usingComponents[name]);
			});
		} else {
			console.warn(`component json file ${json} not exists!`);
		}
		return this;
	}

	seekSubPackage(subPackage) {
		const {root, pages} = subPackage;
		pages.map(page => pathUtil.join(root, page)).forEach(page => {
			this.seekPage(page);
		});
		return this;
	}

	getEntryPoints() {
		return _.keys(this.entryPoints)
			.filter(point => !this.plugin.webpackEntryScripts[point])
			.map(point => this.entryPoints[point]);
	}

	getAssets() {
		const items = {};
		_.keys(this.assets).forEach(key => {
			if (!this.plugin.webpackEntryAssets[key])
				items[key] = this.assets[key];
		});
		return items;
	}

	diffEntries() {
		if (!this.__difference__) {
			const plugin = this.plugin;
			const addScripts = _.keys(this.entryPoints).filter(entry => {
				return !plugin.webpackEntryScripts[entry];
			});
			const addAssets = _.keys(this.assets).filter(key => {
				return !plugin.webpackEntryAssets[key];
			});

			const removeChunks = [], removeScripts = [];
			_.keys(plugin.webpackEntryScripts).forEach(key => {
				if (!this.entryPoints[key]) {
					removeChunks.push(removeExt(key));
					removeScripts.push(key);
				}
			});

			const removeAssets = _.keys(plugin.webpackEntryAssets).filter(key => {
				return !this.assets[key];
			});

			this.__difference__ = {
				add:    {
					count:   addScripts.length + addAssets.length,
					scripts: addScripts,
					assets:  addAssets,
				},
				remove: {
					count:   removeScripts.length + removeAssets.length,
					scripts: removeScripts,
					assets:  removeAssets,
					chunks:  removeChunks,
				},
			};
		}
		return this.__difference__;
	}

	hasNewEntries() {
		if (this.getEntryPoints().length > 0) return true;
		if (_.size(this.getAssets()) > 0) return true;
		return false;
	}
}

module.exports = MiniProgramResources;
